]> dgit.raspbian.org Git - libreoffice.git/commitdiff
firebird-db-connection
authorDebian LibreOffice Maintainers <debian-openoffice@lists.debian.org>
Sat, 3 Oct 2026 10:46:08 +0000 (12:46 +0200)
committerRene Engelhard <rene@debian.org>
Sat, 3 Oct 2026 10:46:08 +0000 (12:46 +0200)
commit 16cf06caff90c0e4d9fe2fc24eac378ed533c48d
Author: Caolán McNamara <caolan.mcnamara@collabora.com>
Date:   Tue Jul 28 11:05:37 2026 +0000

    Resolves: tdf#172935 Connection from Base to external firebird database refused

    there is one firebird engine per process and it reads its configuration once
    from the directory we set up, so a .fdb the user picked somewhere else could
    not be attached. firebird resolves a name in databases.conf before it treats
    it as a path, so give the file a name there and pass firebird that name,
    dropped again when the connection is disposed.

    since:

    commit 520d8173cef39a4da85b3ba21bdaaa3150384c25
    Date:   Sun Jul 12 12:10:41 2026 +0000

        firebird: keep each embedded database's files in one directory

    Change-Id: I4872a4e2165c4701bfb20be5dcd8c119b1723125
Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
    Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/7990
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
    (cherry picked from commit 6ab71a109bd8a8335208c9faa37a3cfa94523148)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208773
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
    (cherry picked from commit 99bf5222896886d062b6a6a67377e7671de1446a)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208855

Gbp-Pq: Name firebird-db-connection.diff

connectivity/source/drivers/firebird/Connection.cxx
connectivity/source/drivers/firebird/Connection.hxx
connectivity/source/drivers/firebird/Driver.cxx
connectivity/source/drivers/firebird/Driver.hxx

index c5e1e958af655ccfa33e925c983806f882e95cc5..edac670bdbfe0c36aea1669b1157b545c507c144 100644 (file)
@@ -22,6 +22,7 @@
 #include "Clob.hxx"
 #include "Connection.hxx"
 #include "DatabaseMetaData.hxx"
+#include "Driver.hxx"
 #include "PreparedStatement.hxx"
 #include "Statement.hxx"
 #include "Util.hxx"
@@ -156,13 +157,14 @@ bool databaseHeaderHasBackupState(const OUString& rDatabasePath)
 }
 
 void Connection::construct(const OUString& url, const Sequence< PropertyValue >& info,
-                           const OUString& rDatabaseDataDirectoryURL)
+                           FirebirdDriver& rDriver)
 {
     ConnectionGuard aGuard(m_refCount);
 
     try
     {
         m_sConnectionURL = url;
+        m_xDriver = &rDriver;
 
         bool bIsNewDatabase = false;
         // the database may be stored as an
@@ -196,7 +198,8 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >&
 
             bIsNewDatabase = !m_xEmbeddedStorage->hasElements();
 
-            m_pDatabaseFileDir.reset(new ::utl::TempFileNamed(&rDatabaseDataDirectoryURL, true));
+            const OUString sDatabaseDataDirectoryURL = rDriver.getDatabaseDataDirectoryURL();
+            m_pDatabaseFileDir.reset(new ::utl::TempFileNamed(&sDatabaseDataDirectoryURL, true));
             m_pDatabaseFileDir->EnableKillingFile();
             m_sFirebirdURL = m_pDatabaseFileDir->GetFileName() + "/firebird.fdb";
             m_sFBKPath = m_pDatabaseFileDir->GetFileName() + "/firebird.fbk";
@@ -250,6 +253,10 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >&
                     bIsNewDatabase = true;
 
                 osl::FileBase::getSystemPathFromFileURL(m_sFirebirdURL, m_sFirebirdURL);
+
+                // The user picked this file, so it is outside the directory firebird is
+                // restricted to. Reach it by the name the driver gives us for it.
+                m_sExternalDatabaseName = rDriver.addExternalDatabaseName(m_sFirebirdURL);
             }
         }
 
@@ -327,12 +334,13 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >&
 
         ISC_STATUS_ARRAY status;            /* status vector */
         ISC_STATUS aErr;
-        const OString sFirebirdURL = OUStringToOString(m_sFirebirdURL, RTL_TEXTENCODING_UTF8);
+        const OString sDatabaseName
+            = OUStringToOString(getFirebirdDatabaseName(), RTL_TEXTENCODING_UTF8);
         if (bIsNewDatabase)
         {
             aErr = isc_create_database(status,
-                                       sFirebirdURL.getLength(),
-                                       sFirebirdURL.getStr(),
+                                       sDatabaseName.getLength(),
+                                       sDatabaseName.getStr(),
                                        &m_aDBHandle,
                                        dpbBuffer.size(),
                                        dpbBuffer.c_str(),
@@ -350,8 +358,8 @@ void Connection::construct(const OUString& url, const Sequence< PropertyValue >&
             }
 
             aErr = isc_attach_database(status,
-                                       sFirebirdURL.getLength(),
-                                       sFirebirdURL.getStr(),
+                                       sDatabaseName.getLength(),
+                                       sDatabaseName.getStr(),
                                        &m_aDBHandle,
                                        dpbBuffer.size(),
                                        dpbBuffer.c_str());
@@ -951,6 +959,12 @@ void Connection::disposing()
 
     storeDatabase();
 
+    if (!m_sExternalDatabaseName.isEmpty())
+    {
+        m_xDriver->removeExternalDatabaseName(m_sExternalDatabaseName);
+        m_sExternalDatabaseName.clear();
+    }
+
     cppu::WeakComponentImplHelperBase::disposing();
 
     m_pDatabaseFileDir.reset();
index c680c20db7370d218bb024e93ca4fd839f2009af..0a56de4fb9e0d89b5d8c19954b73aafd13c60147 100644 (file)
@@ -25,6 +25,7 @@
 #include <cppuhelper/compbase.hxx>
 #include <cppuhelper/weakref.hxx>
 #include <memory>
+#include <rtl/ref.hxx>
 #include <OTypeInfo.hxx>
 #include <unotools/tempfile.hxx>
 
@@ -128,6 +129,21 @@ namespace connectivity::firebird
             /** We are using an external (local) file */
             bool                m_bIsFile;
 
+            /** The driver that made this connection, and owns the directory firebird works in. */
+            rtl::Reference<FirebirdDriver> m_xDriver;
+
+            /**
+             * The name firebird was given for a database outside its own directory, empty when the
+             * database is inside it and its path was handed over directly.
+             */
+            OUString            m_sExternalDatabaseName;
+
+            /** The name or path this connection hands to firebird to reach its database. */
+            const OUString& getFirebirdDatabaseName() const
+            {
+                return m_sExternalDatabaseName.isEmpty() ? m_sFirebirdURL : m_sExternalDatabaseName;
+            }
+
             /** When true the embedded database was opened in this session, so the
                 temporary .fdb holds live data that must be written back into the .odb
                 when the connection is disposed. */
@@ -169,7 +185,7 @@ namespace connectivity::firebird
             /// @throws css::uno::RuntimeException
             void construct( const OUString& url,
                                     const css::uno::Sequence< css::beans::PropertyValue >& info,
-                                    const OUString& rDatabaseDataDirectoryURL);
+                                    FirebirdDriver& rDriver);
 
             const OUString& getConnectionURL()  const   {return m_sConnectionURL;}
             bool            isEmbedded()        const   {return m_bIsEmbedded;}
index 1f345d772ac6b0c80f6f8d62817345af6051cb5c..d02de40377b7dccf45819a2f2fc8f14d075129e4 100644 (file)
 #include <strings.hrc>
 #include <resource/sharedresources.hxx>
 
+#include <config_firebird.h>
+
+// firebird's own header has functions with parameters it does not use
+#if defined __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#if defined _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4100) // unreferenced formal parameter
+#endif
+#include <firebird/Interface.h>
+#if defined _MSC_VER
+#pragma warning(pop)
+#endif
+#if defined __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 #include <comphelper/servicehelper.hxx>
 #include <cppuhelper/supportsservice.hxx>
 #include <osl/file.hxx>
 #include <osl/process.h>
+#include <osl/thread.h>
 #include <rtl/bootstrap.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/uuid.h>
 #include <sal/log.hxx>
 
+#include <string_view>
+
 using namespace com::sun::star;
 using namespace com::sun::star::uno;
 using namespace com::sun::star::lang;
@@ -52,6 +76,66 @@ constexpr OUString our_sFirebirdRootVar = u"FIREBIRD"_ustr;
 #ifdef MACOSX
 constexpr OUString our_sFirebirdLibVar = u"LIBREOFFICE_FIREBIRD_LIB"_ustr;
 #endif
+
+// The name of the file in the firebird directory that maps a name to the absolute path of a
+// database. firebird looks a name up here before it treats it as a path, and re-reads the file
+// whenever it has changed.
+constexpr OUString our_sDatabasesConfName = u"databases.conf"_ustr;
+
+// Put rContent in the file at rURL, discarding whatever was there before.
+bool writeFile(const OUString& rURL, std::string_view aContent)
+{
+    ::osl::File aFile(rURL);
+    if (aFile.open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write) != ::osl::FileBase::E_None)
+    {
+        if (aFile.open(osl_File_OpenFlag_Write) != ::osl::FileBase::E_None)
+            return false;
+        if (aFile.setSize(0) != ::osl::FileBase::E_None)
+        {
+            aFile.close();
+            return false;
+        }
+    }
+
+    bool bWholeContentWritten = true;
+    if (!aContent.empty())
+    {
+        sal_uInt64 nWritten = 0;
+        bWholeContentWritten
+            = aFile.write(aContent.data(), aContent.size(), nWritten) == ::osl::FileBase::E_None
+              && nWritten == aContent.size();
+    }
+    aFile.close();
+    return bWholeContentWritten;
+}
+
+// firebird takes its root directory from the FIREBIRD variable on its first use, and reads the
+// firebird.conf that confines it to that directory only from there. Ask firebird which directory
+// it settled on and treat anything but our own as unconfined.
+bool isFirebirdRootedAt(const OUString& rDataDirPath)
+{
+    Firebird::IMaster* pMaster = Firebird::fb_get_master_interface();
+    if (!pMaster)
+        return false;
+
+    Firebird::IConfigManager* pConfigManager = pMaster->getConfigManager();
+    if (!pConfigManager)
+        return false;
+
+    const char* pRootDirectory = pConfigManager->getRootDirectory();
+    if (!pRootDirectory)
+        return false;
+
+    OUString sRootPath = OStringToOUString(pRootDirectory, osl_getThreadTextEncoding());
+    if (sRootPath.endsWith("/") || sRootPath.endsWith("\\"))
+        sRootPath = sRootPath.copy(0, sRootPath.getLength() - 1);
+
+#if defined(_WIN32)
+    return sRootPath.equalsIgnoreAsciiCase(rDataDirPath);
+#else
+    return sRootPath == rDataDirPath;
+#endif
+}
 };
 
 FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentContext >& _rxContext)
@@ -90,25 +174,25 @@ FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentCo
     sDataDirPath = sDataDirPath.toAsciiUpperCase();
 #endif
 
+    // ExternalFileAccess is already None by default. Saying so here keeps a database firebird
+    // opens from reading databases.conf back out of the directory it shares with it.
     OString sConf = "DatabaseAccess = Restrict "
-        + OUStringToOString(sDataDirPath, RTL_TEXTENCODING_UTF8) + "\n";
+        + OUStringToOString(sDataDirPath, RTL_TEXTENCODING_UTF8)
+        + "\nExternalFileAccess = None\n";
 
     OUString sConfURL = m_firebirdDataDirectory.GetURL() + "/firebird.conf";
-    ::osl::File aConfFile(sConfURL);
-    if (aConfFile.open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write) == ::osl::FileBase::E_None)
+    if (writeFile(sConfURL, std::string_view(sConf))
+        // An empty databases.conf, so that firebird has the file in hand from its first use and
+        // notices the names added to it later.
+        && writeFile(m_firebirdDataDirectory.GetURL() + "/" + our_sDatabasesConfName,
+                     std::string_view()))
     {
-        sal_uInt64 nWritten = 0;
-        if (aConfFile.write(sConf.getStr(), sConf.getLength(), nWritten) == ::osl::FileBase::E_None
-            && nWritten == static_cast<sal_uInt64>(sConf.getLength()))
-        {
-            osl_setEnvironment(our_sFirebirdRootVar.pData, sDataDirPath.pData);
-            m_bConfined = true;
-        }
-        aConfFile.close();
+        osl_setEnvironment(our_sFirebirdRootVar.pData, sDataDirPath.pData);
+        m_bConfined = isFirebirdRootedAt(sDataDirPath);
     }
 
     SAL_WARN_IF(!m_bConfined, "connectivity.firebird",
-                "could not write " << sConfURL << ", firebird connections will be refused");
+                "firebird is not confined to " << sDataDirPath << ", connections will be refused");
 
 #ifndef SYSTEM_FIREBIRD
     // Overrides firebird's hardcoded default of /usr/local/firebird on *nix,
@@ -132,6 +216,51 @@ FirebirdDriver::FirebirdDriver(const css::uno::Reference< css::uno::XComponentCo
 
 FirebirdDriver::~FirebirdDriver() = default;
 
+OUString FirebirdDriver::addExternalDatabaseName(const OUString& rDatabasePath)
+{
+    MutexGuard aGuard(m_aMutex);
+
+    sal_uInt8 aUuid[16];
+    rtl_createUuid(aUuid, nullptr, false);
+
+    OUStringBuffer aName(u"database");
+    for (const sal_uInt8 nByte : aUuid)
+    {
+        aName.append(OUString::number(nByte >> 4, 16) + OUString::number(nByte & 0x0F, 16));
+    }
+    const OUString sName = aName.makeStringAndClear();
+
+    m_aExternalDatabaseNames.emplace(sName, rDatabasePath);
+    writeExternalDatabaseNames();
+
+    return sName;
+}
+
+void FirebirdDriver::removeExternalDatabaseName(const OUString& rName)
+{
+    MutexGuard aGuard(m_aMutex);
+
+    if (m_aExternalDatabaseNames.erase(rName) != 0)
+        writeExternalDatabaseNames();
+}
+
+void FirebirdDriver::writeExternalDatabaseNames()
+{
+    MutexGuard aGuard(m_aMutex);
+
+    OStringBuffer aContent;
+    for (const auto& [rName, rPath] : m_aExternalDatabaseNames)
+    {
+        aContent.append(OUStringToOString(rName, RTL_TEXTENCODING_UTF8) + " = "
+                        + OUStringToOString(rPath, RTL_TEXTENCODING_UTF8) + "\n");
+    }
+    const OString sContent = aContent.makeStringAndClear();
+
+    const OUString sURL = m_firebirdDataDirectory.GetURL() + "/" + our_sDatabasesConfName;
+    SAL_WARN_IF(!writeFile(sURL, std::string_view(sContent)), "connectivity.firebird",
+                "could not write " << sURL);
+}
+
 void FirebirdDriver::disposing()
 {
     MutexGuard aGuard(m_aMutex);
@@ -199,7 +328,7 @@ Reference< XConnection > SAL_CALL FirebirdDriver::connect(
     }
 
     rtl::Reference<Connection> pCon = new Connection();
-    pCon->construct(url, info, getDatabaseDataDirectoryURL());
+    pCon->construct(url, info, *this);
 
     m_xConnections.emplace_back(*pCon);
 
index 0f5172f6222cb2aafd24fb9e0f067cd6c65d9fcb..060ca5d90fea7a05b4756448d47ddc731aad6eca 100644 (file)
@@ -28,6 +28,8 @@
 #include <cppuhelper/compbase.hxx>
 #include <unotools/tempfile.hxx>
 
+#include <map>
+
 namespace connectivity::firebird
     {
         // The SQL dialect in use
@@ -50,6 +52,14 @@ namespace connectivity::firebird
             ::utl::TempFileNamed m_firebirdDataDirectory;
             bool m_bConfined;
 
+            // The names firebird may use for a database that lies outside its own directory, each
+            // mapped to that database's absolute path. The names are random, and only the databases
+            // the user picked in this session are in here.
+            std::map<OUString, OUString> m_aExternalDatabaseNames;
+
+            // Write m_aExternalDatabaseNames out as the databases.conf of the firebird directory.
+            void writeExternalDatabaseNames();
+
         protected:
             ::osl::Mutex                m_aMutex;       // mutex is need to control member access
             OWeakRefArray               m_xConnections; //  vector containing a list
@@ -67,6 +77,17 @@ namespace connectivity::firebird
             // associated files it creates for them under this directory as well.
             OUString getDatabaseDataDirectoryURL() const { return m_firebirdDataDirectory.GetURL(); }
 
+            // Give firebird a name for a database that lies outside its own directory, and return
+            // that name. firebird resolves a name it finds in databases.conf without applying the
+            // DatabaseAccess restriction, while every path it is handed elsewhere, a difference file
+            // or a shadow, is still checked against that restriction. The name is random so that a
+            // database firebird opens cannot ask for another database by guessing the name.
+            OUString addExternalDatabaseName(const OUString& rDatabasePath);
+
+            // Take back a name from addExternalDatabaseName. Any database already attached through
+            // it stays open.
+            void removeExternalDatabaseName(const OUString& rName);
+
             // OComponentHelper
             virtual void SAL_CALL disposing() override;